package com.smgeek.gkrpc.server;

import com.smgeek.gkrpc.ReflectionUtil;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.Response;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.sgrpc.transport.RequestHandler;
import com.smgeek.sgrpc.transport.TransportServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;

@Slf4j
public class RpcServer {
    private RpcServerConfig config;
    private TransportServer net;
    private Encoder encoder;
    private Decoder decoder;
    private ServiceManager serviceManager;
    private ServiceInvoker serviceInvoker;

    public RpcServer() {
        this(new RpcServerConfig());
    }

    public RpcServer(RpcServerConfig config) {
        this.config = config;

        // net
        this.net = ReflectionUtil.newInstance(config.getTransportServerClass());
        this.net.init(config.getPort(), this.handler);

        // codec
        this.encoder = ReflectionUtil.newInstance(config.getEncoderClass());
        this.decoder = ReflectionUtil.newInstance(config.getDecoderClass());

        // service
        this.serviceManager = new ServiceManager();
        this.serviceInvoker = new ServiceInvoker();
    }

    public <T> void register(Class<T> interfaceClass, T bean) {
        serviceManager.register(interfaceClass, bean);
    }

    public void start() {
        this.net.start();
    }

    public void stop() {
        this.net.stop();
    }

    private RequestHandler handler = (receive, toResp) -> {
        Response resp = new Response();
        try {
            byte[] inBytes = IOUtils.readFully(receive, receive.available());
            Request request = decoder.decoder(inBytes, Request.class);
            log.info(">>> get request:{}", request);

            ServiceInstance sis = serviceManager.lookup(request);
            Object ret = serviceInvoker.invoke(sis, request);
            resp.setData(ret);
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
            resp.setCode(1);
            resp.setMessage("RpcServer got error:" + e.getClass().getName()
                    + ":" + e.getMessage());
        } finally {
            try {
                byte[] outBytes = encoder.encode(resp);
                toResp.write(outBytes);

                log.info("response client");
            } catch (IOException e) {
                log.warn(e.getMessage(), e);
            }
        }
    };
}
